import fg from "fast-glob";
import fs from "fs-extra";
import path from "path";
import { toolchain } from "../toolchain.js";
import normalizePath from "normalize-path";

export class HardCodeCollector {
    private static usedBySrcMap: { [pic: string]: string } | null = null;

    /**
     * 
     * @param file Relative to Assets/AssetSources or Absolute path.
     * @returns 
     */
    public static isUsed(file: string): string | null {
        if (HardCodeCollector.usedBySrcMap == null) {
            throw 'Call ensureData first';
        }
        // if (file.includes('Draw.png')) {
        //     console.log('ddd');
        // }
        if (path.isAbsolute(file)) {
            const assetSourcesRoot = path.join(toolchain.opts.projectRoot, 'Assets/AssetSources');
            file = path.relative(assetSourcesRoot, file);
        }
        file = normalizePath(file);
        const a = HardCodeCollector.usedBySrcMap[(file).toLowerCase()];
        if (a) return a;
        const ext = path.extname(file);
        const bn = path.basename(file, ext);
        return HardCodeCollector.usedBySrcMap[bn.toLowerCase()];
    }


    public static async ensureData(): Promise<void> {
        if (HardCodeCollector.usedBySrcMap != null) {
            return;
        }
        HardCodeCollector.usedBySrcMap = {};

        // 收集asset atlas
        const atlasMap = await HardCodeCollector.collectAssetAtlas();
        
        const assetSourcesRoot = path.join(toolchain.opts.projectRoot, 'Assets/AssetSources');
        const srcs = await fg(['**/*.ts', '**/*.cs'], { cwd: toolchain.opts.projectRoot, 
            ignore: ['Library/**/*', '**/*.d.ts', '**/Editor/**/*.cs', '**/Plugins/**/*.cs', 'Assets/Gen/**/*.cs', '**/KeyWord.ts', '**/AssetAutoDownloader.ts'] });
        for (const src of srcs) {
            const file = path.join(toolchain.opts.projectRoot, src);
            // if (src.endsWith('SceneMapView.ts')) {
            //     console.log('aaaaa');
            // }
            const content = await fs.readFile(file, 'utf-8');
            const aaaMchs = content.matchAll(/(\w+)\.atlas\s*=\s*AllAssetAtlas\.instance\.(\w+)/g);
            const var2atlasMap: { [varName: string]: string[] } = {};
            for (const aaaMch of aaaMchs) {
                let arr = var2atlasMap[aaaMch[1]];
                if (!arr) {
                    arr = [];
                    var2atlasMap[aaaMch[1]] = arr;
                }
                arr.push(aaaMch[2]);
            }

            const lines = content.replaceAll(/\/\/[^\r\n]+/g, '').split(/\r?\n/);
            for (let i = 0, len = lines.length; i < len; i++) {
                const line = lines[i];
                if (line.startsWith('import ') || line.includes('elems.get') || line.includes('this.getElement') || line.includes('ElemFinder.find') || line.includes('this.initImage') || line.includes('this.addTimer')) {
                    continue;
                }

                let atlasFiles: string[] | null = null;
                const atlasMch = line.match(/(\w+)\.skin\s*=/);
                if (atlasMch) {
                    const varName = atlasMch[1];
                    const arr = var2atlasMap[varName];
                    if (arr) {
                        atlasFiles = arr.map(v => atlasMap[v]);
                    }
                }

                const mchs = line.matchAll(/('|"|`)\S+?\1/g);
                for (const mch of mchs) {
                    const str = mch[0];
                    const quoted = str.substring(1, str.length - 1).toLowerCase();
                    // if (quoted == 'lingquan_') {
                    //     console.log('aaaaa');
                    // }
                    if (atlasFiles || quoted.endsWith('.png') || quoted.endsWith('.jpg') || quoted.endsWith('.prefab') || quoted.endsWith('.asset') || quoted.endsWith('.bytes')) {
                        // 目前仅处理简单的情况，其它复杂情况暂不考虑
                        let pattern: string;
                        if (quoted.includes('{0}')) {
                            // 处理类似'ui/texture/mini{0}.png'
                            pattern = quoted.replace(/\{\d+\}/g, '*');
                        } else {
                            // 处理类似'ui/texture/' + platform[fysdk.ins.platform].tolowercase() + '_hall_bg_' + this.bgoutfitname() + '.png'
                            pattern = quoted.replaceAll(/\$\{.+\}/g, '*').replace(/['"]\s*\+.+?\+\s*['"]/g, '*');
                        }
                        if (pattern != null && atlasFiles) {
                            if (line.includes(str + ' +')) {
                                pattern += '*';
                            }
                            const pics = await fg(atlasFiles.map(v => v + '/' + pattern + '.*'), { cwd: assetSourcesRoot, caseSensitiveMatch: false });
                            for (const p of pics) {
                                HardCodeCollector.usedBySrcMap[p.toLowerCase()] = `${file}:${i + 1}`;
                            }
                        } else if (pattern != null && pattern.includes('/')) {
                            if (pattern.includes('/*.png') || pattern.includes('/*.jpg') || pattern.includes('/*.prefab') || pattern.includes('/*.asset') || pattern.includes('/*.bytes')) {
                                console.warn('本模式将导致所有文件被认定为有效：', pattern);
                                console.warn(file);
                                // process.exit(1);
                            }
                            if (!pattern.match(/^\*\*\/\*\.\w+$/)) {
                                const pics = await fg(pattern, { cwd: assetSourcesRoot, caseSensitiveMatch: false });
                                for (const p of pics) {
                                    // console.log('mark used by code: ', pic, ', hit: ', p);
                                    // if (p.toLowerCase() == 'connect') {
                                    //     console.log('aaaaa');
                                    // }
                                    HardCodeCollector.usedBySrcMap[p.toLowerCase()] = `${file}:${i + 1}`;
                                }
                            }
                        } else {
                            HardCodeCollector.usedBySrcMap[quoted] = `${file}:${i + 1}`;
                        }
                    } else {
                        HardCodeCollector.usedBySrcMap[quoted] = `${file}:${i + 1}`;
                    }
                }
            }
        }
    }

    private static async collectAssetAtlas(): Promise<Record<string, string>> {
        const out: Record<string, string> = {};
        const aaa = path.join(toolchain.opts.projectRoot, 'TsScripts/System/atlas/AllAssetAtlas.ts');
        if (!fs.existsSync(aaa)) {
            console.log('AllAssetAtlas.ts not found');
            return out;
        }

        const aaaContent = await fs.readFile(aaa, 'utf-8');
        const mchs = aaaContent.matchAll(/readonly (\w+)(?:\s?:\s?AssetAtlas)?\s?=\s?new AssetAtlas\(('|")(\S+)\2\)/g);
        for (const mch of mchs) {
            const name = mch[1];
            const atlas = mch[3];
            out[name] = atlas;
        }
        return out;
    }
}
